package hhm.android.http;

import android.os.Build;
import android.text.TextUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import hhm.android.library.LogUtils;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

public abstract class RetrofitClient<T> {

    protected abstract void onePointLogin();

    protected abstract String getToken();

    public Retrofit retrofit;

    public RetrofitClient(String url) {
        retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .baseUrl(url)
                .client(getHttpClient())
                .build();
    }

    abstract T getInterfaceService();

    /*
     * 自定义OkHttpClient 设置超时和LOG
     *
     */
    private OkHttpClient getHttpClient() {
        String token = getToken();
        if (TextUtils.isEmpty(token)) {
            token = "";
        }
        String finalToken = token;
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        LogUtils.Companion.d("OkHttp", finalToken);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            return new OkHttpClient.Builder()
                    .connectTimeout(120, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(20, TimeUnit.SECONDS)
                    .addInterceptor(logging)
                    .addInterceptor(chain -> {
                        Request request = chain.request()
                                .newBuilder()
                                .addHeader("accessToken", finalToken)
                                .addHeader("Authorization", "Bearer " + finalToken)
                                .build();
                        Response proceed = chain.proceed(request);
                        int code = proceed.code();
                        if (code == 401) {
                            onePointLogin();
                        }
                        return proceed;
                    })
                    .retryOnConnectionFailure(true)
                    .sslSocketFactory(getSslScoketFactory(), xtm)
                    .hostnameVerifier(getHostnameVerifier())
                    .build();
        } else {
            return new OkHttpClient.Builder()
                    .connectTimeout(120, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(20, TimeUnit.SECONDS)
                    .addInterceptor(logging)
                    .addInterceptor(chain -> {
                        Request request = chain.request()
                                .newBuilder()
                                .addHeader("accessToken", finalToken)
                                .addHeader("Authorization", "Bearer " + finalToken)
                                .build();
                        Response proceed = chain.proceed(request);
                        int code = proceed.code();
                        if (code == 401) {
                            onePointLogin();
                        }
                        return proceed;
                    })
                    .retryOnConnectionFailure(true)
                    .sslSocketFactory(getSslScoketFactory())
                    .hostnameVerifier(getHostnameVerifier())
                    .build();
        }
    }

    private HostnameVerifier getHostnameVerifier() {
        return (hostname, session) -> true;
    }


    private SSLSocketFactory getSslScoketFactory() {

        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");

            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());

        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return sslContext.getSocketFactory();
    }

    X509TrustManager xtm = new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    };

    /*
     * 接口数据转换器，该转换器不做任何转换，直接输出JSON字符串
     */
    private static class RawJsonConverterFactory extends Converter.Factory {

        public static RawJsonConverterFactory create() {
            return new RawJsonConverterFactory();
        }

        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            return new RawJsonResponseBodyConverter();
        }

        @Override
        public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
            return super.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
        }

        @Override
        public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            return super.stringConverter(type, annotations, retrofit);
        }
    }


    private static class RawJsonResponseBodyConverter<String> implements Converter<ResponseBody, String> {

        public RawJsonResponseBodyConverter() {

        }

        @Override
        public String convert(ResponseBody value) throws IOException {

            return (String) value.string();
        }
    }
}
